package org.azj.ColumnInfoReader;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.DuplicateFormatFlagsException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import org.azj.inireader.FieldBasicInfo;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.ht.entity.Column;

public class ColumnInfoReader {
	
	// 用于存储结果的HASH表，key 是中文名
	protected HashMap<String, HashMap<String, FieldBasicInfo>> infoContainerChnSet = 
			new LinkedHashMap<String, HashMap<String, FieldBasicInfo>>();
	// 用于存储结果的HASH表，key 是英文名
	protected HashMap<String, HashMap<String, FieldBasicInfo>> infoContainerEngSet = 
			new LinkedHashMap<String, HashMap<String, FieldBasicInfo>>();
	
	// 保存在界面中必须要填写的内容项.key是"表名.列名"的形式，value暂时为空
	HashMap<String, String> cannotOmitItems;
	
	// 保存XML节点名，以遍历顺序存储
	Vector<String> nodeNameList;
	// 可以在界面上显示的节点的信息列表
	Vector<Column> showableNodesInfo;
	// 不可以在界面上显示的非叶子节点信息列表
	Vector<Column> unshowableMixedNodesInfo;

	// xml的root元素
	Element root;
	
	// 临时存放变量
	HashMap<String, FieldBasicInfo> infoContainerChn;
	HashMap<String, FieldBasicInfo> infoContainerEng;
	String currentTableName;
	
	// 标志read函数是否被读过
	boolean hasReadXml;
	
	public ColumnInfoReader() {
		hasReadXml = false;
	}
	
	public void read(String fileFullPath) 
			throws FileNotFoundException, IOException, DocumentException, XmlFormatException
	{
		if (false == checkFileExists(fileFullPath) )
		{
			throw new FileNotFoundException("文件" + fileFullPath + "没有找到");
		}
		
		cannotOmitItems = new HashMap<String, String>();
		nodeNameList = new Vector<String>();
		showableNodesInfo = new Vector<Column>();
		unshowableMixedNodesInfo = new Vector<Column>();
		
		Document document = new SAXReader().read(new File(fileFullPath));
		root = document.getRootElement();
		
		parseElements(root, true);
		
		hasReadXml = true;
	}
	
	private boolean checkFileExists(String filepath)
	{
		return new File(filepath).exists();
	}
	
	private void parseElements(Element e, boolean tblNeedShow) throws XmlFormatException
	{
		// 此表是否需要在界面上显示
		boolean bTblNeedShow = tblNeedShow;
		
		List<Element> els = e.elements();
		for (Element element : els) {
			
			Attribute tableName = element.attribute("tbl");
			if (tableName != null){
				// 每次一开始走到表的地方
				bTblNeedShow = true;
				
				createNewSection(tableName.getValue());
				
				// 检查该表是否需要显示在界面上
				Attribute showSig = element.attribute("show");
				if (showSig != null){
					bTblNeedShow = !(showSig.getValue().equals("0"));
				}
				
				// 判断是否复合节点
				if (element.hasMixedContent()) {
					// 非叶子节点，且不具有id号。说明该节点是表头的一部分
					if (bTblNeedShow == false)
					{
						Column column = new Column();
						column.cName = element.attributeValue("text");
						column.eName = element.attributeValue("id");
						unshowableMixedNodesInfo.add(column);
					}
					
					parseElements(element, bTblNeedShow);
				}
				else
				{
					throw new XmlFormatException(tableName.getValue(), "带有tbl属性的节点不能是叶子节点");
				}
				
				// 不应该再有其他属性了
				continue;
			}
			
			Attribute nodeChnText = element.attribute("text");
			if (nodeChnText != null){
				Attribute nodeEngText = element.attribute("id");
				if (nodeEngText != null)
				{
					Attribute nodeNoOmit = element.attribute("no_omit");
					boolean canOmit = nodeNoOmit != null ? false : true;
					
					generateColumnInfo(
							nodeChnText.getValue(), 
							nodeEngText.getValue(), 
							currentTableName, 
							canOmit,
							bTblNeedShow);
				}
				else
				{
					// 如果该node没有子节点，则他一定需要有id属性
					if (element.hasMixedContent() == false)
					{
						throw new XmlFormatException(
								nodeChnText.getValue(), "缺少id属性");	
					}
					else 
					{
						// 非叶子节点，且不具有id号。说明该节点是表头的一部分
						if (tblNeedShow == false)
						{
							Column column = new Column();
							column.cName = element.attributeValue("text");
							column.eName = element.attributeValue("id");
							unshowableMixedNodesInfo.add(column);
						}
					}
				}
				
				// 判断是否复合节点
				if (element.hasMixedContent()) 
				{
					parseElements(element, bTblNeedShow);
				}
			}
			else
			{
				// 没有text的node是那些描述性node，非表示column info的node
				// 判断是否复合节点
				if (element.hasMixedContent()) 
				{
					parseElements(element, bTblNeedShow);
				}
				else 
				{
					throw new XmlFormatException("", "结构性错误，不带有text和id的节点一定不能是叶子节点");	
				}
			}
		}
	}
	
	private void createNewSection(String sectionName) 
	{
		// a new table
		infoContainerChn = new HashMap<String, FieldBasicInfo>();
		infoContainerEng = new HashMap<String, FieldBasicInfo>();
		
    	infoContainerChnSet.put(sectionName, infoContainerChn);
    	infoContainerEngSet.put(sectionName, infoContainerEng);
    	
    	currentTableName = sectionName;
	}
	
	private void generateColumnInfo(
			String desInChn, 
			String nameInEng, 
			String currentTableName, 
			boolean canOmit,
			boolean needShow)
	{
		FieldBasicInfo curBasicInfo = new FieldBasicInfo(
	    		currentTableName, 
	    		nameInEng, 
	    		desInChn, 
	    		canOmit);
	    
	    // 插入结果集
	    if (infoContainerChn.containsKey(desInChn.trim()))
	    {
	    	throw new  DuplicateFormatFlagsException(
	    		String.format("duplicate keys[%s] in table[%s]", 
	    				desInChn.trim(), currentTableName));
	    }
	    else 
	    {
	    	infoContainerChn.put(desInChn.trim(), curBasicInfo);
	    }
	    
	    // 插入结果集
	    if (infoContainerEng.containsKey(curBasicInfo.FieldName.trim()))
	    {
	    	throw new  DuplicateFormatFlagsException(
	    		String.format("duplicate field name [%s] in table[%s]", 
	    		curBasicInfo.FieldName.trim(), currentTableName));
	    }
	    else 
	    {
	    	infoContainerEng.put(curBasicInfo.FieldName.trim(), curBasicInfo);
	    }
	    
	    // value暂时填写为空
	    cannotOmitItems.put(nameInEng, nameInEng);
	    
	    nodeNameList.add(desInChn);
	    
	    // 生成界面上可以显示的对象
	    if (needShow) {
	    	Column cl = new Column();
		    cl.cName = desInChn;
		    cl.eName = nameInEng;
		    showableNodesInfo.add(cl);	
	    }
	}
	
	/*
	* Description: get value by key
	*/
	public FieldBasicInfo getValueByDescription(String tableName, String des) {
	   if (hasReadXml == false) {
		   return null;
	   }
	   
	   return (FieldBasicInfo)infoContainerChnSet.get(tableName).get(des);
	}
	
	/*
	* Description: get value by key
	*/
	public FieldBasicInfo getValueByField(String tableName, String field) {
	   if (hasReadXml == false) {
		   return null;
	   }
	   
	   return (FieldBasicInfo)infoContainerEngSet.get(tableName).get(field);
	}
	
	/*
	* Description: get all containing informations, the key is Chinese description
	*/
	public HashMap<String, FieldBasicInfo> getValuesByDescription() {
	   if (hasReadXml == false) {
		   return null;
	   }
	   
	   return extractFromInfoSet(infoContainerChnSet);
	}
	
	public HashMap<String, FieldBasicInfo> getValuesByDescription(String tableName) {
	   if (hasReadXml == false) {
		   return null;
	   }
		   
	   return infoContainerChnSet.get(tableName);
	}

	private HashMap<String, FieldBasicInfo> extractFromInfoSet(
			HashMap<String, HashMap<String, FieldBasicInfo>> infoSet) {
		
		HashMap<String, FieldBasicInfo> tmpHashMap = 
				   new HashMap<String, FieldBasicInfo>();
			
		   Set<String> keys = infoSet.keySet();
		   Iterator<String> it = keys.iterator();
		   while (it.hasNext()) {
				String tableName = it.next();
				HashMap<String, FieldBasicInfo> tmpTableInfo = infoSet.get(tableName);
				
				Set<String> deskeys = tmpTableInfo.keySet();
				Iterator<String> itBasicInfoDes = deskeys.iterator();
				while(itBasicInfoDes.hasNext()) {
					String desNameString = itBasicInfoDes.next();
					tmpHashMap.put(desNameString, tmpTableInfo.get(desNameString));
				}
		   }
		return tmpHashMap;
	}
	
	/*
	* Description: get all containing infos, the key is Fields name
	*/
	public HashMap<String, FieldBasicInfo> getValuesByFields() {
	   if (hasReadXml == false) {
		   return null;
	   }
	   
	   return extractFromInfoSet(infoContainerEngSet);
	}
	
	public HashMap<String, FieldBasicInfo> getValuesByFields(String tableName) {
		if (hasReadXml == false) {
		   return null;
		}
		   
		return infoContainerEngSet.get(tableName);
	}
	
	/*
	* Description: get all containing information in Chinese
	*/
	public Set<String> getKeysInDescription() {
	   if (hasReadXml == false) {
		   return null;
	   }
	   
	   return extractFromInfoSet(infoContainerChnSet).keySet();
	}
	
	public Set<String> getKeysInDescription(String tableName) {
		if (hasReadXml == false) {
			return null;
		}
		   
		return infoContainerChnSet.get(tableName).keySet();
	}
	
	/*
	* Description: get all containing information in English
	*/
	public Set<String> getKeysInFields() {
	   if (hasReadXml == false) {
		   return null;
	   }
	   
	   return extractFromInfoSet(infoContainerEngSet).keySet();
	}
	
	public Set<String> getKeysInFields(String tableName) {
		if (hasReadXml == false) {
			return null;
		}
		   
		return infoContainerEngSet.get(tableName).keySet();
	}

	// 获取那些不能够被省略的项
	public HashMap<String, String> getCannotOmitItems()
	{
		if (hasReadXml == false) {
			return null;
		}
		   
		return cannotOmitItems;
	}
	
	public Vector<String> getNodeList()
	{
		if (hasReadXml == false) {
			return null;
		}
		   
		return nodeNameList;
	}
	
	public Vector<Column> getNodeInEandC()
	{
		if (hasReadXml == false) {
			return null;
		}
		   
		return showableNodesInfo;
	}
	
	public Element getRootElement()
	{
		if (hasReadXml == false) {
			return null;
		}
		   
		return root;
	}
	
	// 获取那些不应该被显示出来的非叶子节点列表
	public Vector<Column> getNoShowMixedElements()
	{
		if (hasReadXml == false) {
			return null;
		}
		   
		return unshowableMixedNodesInfo;
	}
}
